package com.common.servlet.abstracts;

import java.io.IOException;
import java.util.Set;
import java.util.logging.Logger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.common.model.abstracts.AbstractRequest;
import com.common.model.abstracts.AbstractValidator;
import com.common.security.abstracts.AbstractError;
import com.common.security.abstracts.AbstractException;
import com.common.security.abstracts.AbstractRuntimeException;
import com.common.security.exceptions.HTTPException;
import com.common.security.exceptions.ParserException;
import com.common.servlet.interfaces.IParser;

public abstract class AbstractServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	private static Logger logger = Logger.getAnonymousLogger();
	
	public final void doGet(HttpServletRequest request,	HttpServletResponse response) throws IOException {
		try {
			AbstractRequest abstractRequest = parseRequest(request);
			AbstractError error = validate(abstractRequest);

			if (error == null) {
				setRequest(abstractRequest);
				handle(request, response);
			} else {
				error(error, response);
			}
		} catch (AbstractException exception) {
			exception(exception, response);
		} catch (AbstractRuntimeException runtimeException) {
			exception(runtimeException, response);
		}
	}

	public final void doPost(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		doGet(request, response);
	}

	public AbstractError validate(AbstractRequest request) {
		if (getValidators() != null) {
			if (getValidators().size() == 0) {
				return null;
			}

			for (AbstractValidator validator : getValidators()) {
				if (!validator.validate(request)) {
					return validator.getError();
				}
			}
		}
		return null;
	}

	public void error(AbstractError error, HttpServletResponse response) {
		try {
			logger.severe(error.getCode() + " " + error.getMessage());
			response.setContentType("text/xml");
			response.getWriter().write(parser().parseError(error));
			response.getWriter().close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * This method converts exceptions into errors and sends them to the client.
	 * 
	 * <p>
	 * This is part of the new architecture as presented. The AbstractServlet converts the
	 * received request message into a Request. It is expected that the parser will throw
	 * exceptions and this method should convert them into errors and respond to the client.
	 * </p>
	 */
	public void exception(AbstractException exception, HttpServletResponse response) {
		try {
			logger.severe(exception.getCode() + " " + exception.getMessage());
			response.setContentType("text/xml");
			response.getWriter().close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * This method converts Runtime Exception into errors and send them to the clients.
	 */
	public void exception(AbstractRuntimeException exception, HttpServletResponse response) {
		try {
			logger.severe(exception.getCode() + " " + exception.getMessage());
			response.setContentType("text/xml");
			response.getWriter().close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * This method will handle the received request if it passes the parsing process
	 * and the validation process.
	 * 
	 * @param request
	 * @param response
	 */
	public abstract void handle(HttpServletRequest request,	HttpServletResponse response);

	/**
	 * This method is used to load the specific request needed by each Servlet.
	 * It is expected to use a safe unchecked cast.
	 * 
	 * @param abstractRequest
	 */
	public abstract void setRequest(AbstractRequest abstractRequest);

	public abstract AbstractRequest parseRequest(HttpServletRequest request) throws ParserException, HTTPException, IOException;

	public abstract Set<AbstractValidator> getValidators();

	public abstract IParser parser();
}
